package com.cntaiping.workutil.prometheus;

import com.cntaiping.workutil.test.FileUtils;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.micrometer.prometheus.PrometheusMeterRegistry;
import io.prometheus.client.CollectorRegistry;
import io.prometheus.client.Gauge;
import org.apache.commons.lang.time.DateFormatUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;

@Component
public class TaskService {
    private static final Logger LOGGER = LoggerFactory.getLogger(TaskService.class);

    @Autowired
    private PrometheusMeterRegistry prometheusMeterRegistry;
    @Autowired
    private MetricsFactory factory;
    @Value("${execute.command}")
    private String command;
    @Value("${execute.commandDir}")
    private String workDir;
    @Value("${execute.metricsTimeoutSeconds}")
    private int metricsTimeoutSeconds;
    @Value("${execute.metricsJson}")
    private String metricsJson;
    @Value("${execute.metricsPrefix}")
    private String prefix;

    @PostConstruct
    public void initMetrics() {
        LOGGER.info("开始初始化指标--");
        Map<String,Object> metrics = initFile(metricsJson);
        BatchVisConRes batchVisConRes = new BatchVisConRes(0, "up_aimanager", "0", "0", "success", "0000", "所有服务检测成功");
        if (metrics.containsKey("0000")) {
            String readMetrics = String.valueOf(metrics.get("0000"));
            readExec(readMetrics, batchVisConRes);
        } else {
            for(String key : metrics.keySet()){
                batchVisConRes.setCode("9999");
                batchVisConRes.setMsg(String.valueOf(metrics.get(key)));
                batchVisConRes.setResult("failed");
            }
            factory.addMetrics(batchVisConRes);
        }

        addMetrics();
    }

    @Scheduled(cron = "${execute.cron.refreshMetrics}")
    public void prometheusMetricsRegistry() throws Exception {


        String path = metricsJson;

        LOGGER.info("*****开始读取test模块检测结果文件任务*****");

        LOGGER.info("文件路径:{}", path);

        /**
         * 判断生成的检测文件是否为空，指标文件修改时间是否超过15分钟 true-为空或超时  生成指标9999
         */
        Map<String,Object> metrics = checkFile(path);

        /**
         * 添加指标
         */
        BatchVisConRes batchVisConRes = new BatchVisConRes(0, "up_aimanager", "0", "0", "success", "0", "所有服务检测成功");
        if (!metrics.containsKey("0000")) {
            for(String key : metrics.keySet()){
                batchVisConRes.setCode("9999");
                batchVisConRes.setMsg(String.valueOf(metrics.get(key)));
                batchVisConRes.setResult("failed");
            }
            factory.remove(batchVisConRes);
            factory.addMetrics(batchVisConRes);
        } else {
            /**
             * 说明检测指标文件正常,将覆盖初始化的指标项
             */
            factory.clearMetricsList();
            String result = org.apache.commons.io.FileUtils.readFileToString(new File(path), StandardCharsets.UTF_8);
            LOGGER.info("文件读取成功: " + path);
            /**
             * 读取检测文件  0000和success为检测成功 key=api_name value=0
             * 失败  value=9999
             */
            readExec(result, batchVisConRes);
        }

        /**
         * 开始往prometheus添加自定义指标
         */
        addMetrics();

    }

    public void addMetrics() {
        List<BatchVisConRes> metricsList = factory.getMetricsList();
        CollectorRegistry prometheusRegistry = prometheusMeterRegistry.getPrometheusRegistry();
        prometheusRegistry.clear();
        if(metricsList.size()>0){
            for (BatchVisConRes batchVisConRes : metricsList) {
                Gauge register;
                int num = "success".equals(batchVisConRes.getResult()) ? 1 : 0;
                register = Gauge.build().name(batchVisConRes.getApiName()).labelNames("num", "code", "costTime", "httpStatus", "msg").help("指标收集程序").create();
                register.labels(String.valueOf(batchVisConRes.getNum()), batchVisConRes.getCode(), batchVisConRes.getCostTime(), batchVisConRes.getHttpStatus(), batchVisConRes.getMsg()).set(num);
                prometheusRegistry.register(register);
                if(num==1) {
                    LOGGER.info(batchVisConRes.getMsg());
                }else {
                    LOGGER.info("检测失败服务名:{}", batchVisConRes.getMsg());
                }
            }
        }

        LOGGER.info("指标注册任务完成");
    }


    public Map<String,Object> checkFile(String execPath) {
        Map<String,Object> metrics = new HashMap<>();
        try {
            String pattern = "yyyy-MM-dd HH:mm:ss";
            File file = new File(execPath);
            long modified = file.lastModified();

            if (file.exists()) {
                /**
                 * 文件存在,判断文件内容是否为空
                 */
                if (0 == file.length()) {
                    LOGGER.error("指标文件内容为空");
                    metrics.put("0001","指标文件内容为空");
                } else {
                    LOGGER.info("开始读取文件---");
                    String last = DateFormatUtils.format(modified, pattern);
                    String now = DateFormatUtils.format(System.currentTimeMillis(), pattern);
                    if (System.currentTimeMillis() - modified > (long) metricsTimeoutSeconds * 1000) {
                        LOGGER.error("文件生成时间已超过15分钟");
                        metrics.put("0002","指标文件生成时间已超过15分钟");
                    }else {
                        metrics.put("0000","指标文件获取正常");
                    }
                    LOGGER.info("当前时间:{},检测文件上次修改时间:{},间隔:{}s", now, last, (System.currentTimeMillis() - modified) / 1000);

                }
            } else {
                /**
                 * 文件不存在
                 */
                LOGGER.error("指标文件不存在");
                metrics.put("0003","指标文件不存在");

            }

        } catch (Exception e) {
            LOGGER.error("检测文件读取校验失败:{}", e.getMessage());
            e.printStackTrace();
            metrics.put("0004","指标文件读取失败");

        }

        return metrics;


    }

    public Map<String,Object> initFile(String initPath) {

        Map<String,Object> metrics = new HashMap<>();
        try {
            File file = new File(initPath);

            if (file.exists()) {
                /**
                 * 文件存在,判断文件内容是否为空
                 */
                if (0 == file.length()) {
                    LOGGER.error("初始化指标文件内容为空");
                    metrics.put("0001","初始化指标文件内容为空");
                } else {
                    /**
                     * 初始化读取文件
                     */
                    String metricsString = org.apache.commons.io.FileUtils.readFileToString(new File(initPath), StandardCharsets.UTF_8);
                    LOGGER.info("初始指标文件读取成功");
                    metrics.put("0000",metricsString);
                }
            } else {
                /**
                 * 文件不存在
                 */
                LOGGER.error("初始化指标文件不存在");
                metrics.put("0002","初始化指标文件不存在");
            }

        } catch (Exception e) {
            LOGGER.error("检测文件读取校验失败:{}", e.getMessage());
            metrics.put("0003","读取指标文件失败");
            e.printStackTrace();
        }

        return metrics;


    }

    public void readExec(String checkResult, BatchVisConRes bat) {
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            StringBuffer buffer = new StringBuffer();
            List<BatchVisConRes> batchVisConResList = objectMapper.readValue(checkResult, new TypeReference<List<BatchVisConRes>>() {});

//        List<ExecMetrics.BatchVisConRes> batchVisConResList = execMetrics.getBatchVisConResList();
            int nameLength = 0;
            int resultLength = 0;
            int numLength = 0;
            for (BatchVisConRes batchVisConRes : batchVisConResList) {
                if (batchVisConRes.getApiName().length() > nameLength) {
                    nameLength = batchVisConRes.getApiName().length();
                }
                if(batchVisConRes.getResult().length()>resultLength){
                    resultLength = batchVisConRes.getResult().length();
                }
                if(String.valueOf(batchVisConRes.getNum()).length()>numLength) {
                    numLength = String.valueOf(batchVisConRes.getNum()).length();
                }
                if(!"success".equals(batchVisConRes.getResult())){
                    buffer.append(batchVisConRes.getApiName()).append("|");
                }
            }
            if(buffer.toString().endsWith("|")){
                String name = buffer.toString();
                name = name.substring(0,name.lastIndexOf("|"));
                bat.setMsg(name);
                bat.setResult("failed");
            }
            factory.addMetrics(bat);

            for (BatchVisConRes batchVisConRes : batchVisConResList) {
                LOGGER.info("检测结果 num=[{}],value=[{}]  [{}],key=[{}]",
                        setPrefixSpace(numLength-String.valueOf(batchVisConRes.getNum()).length(),String.valueOf(batchVisConRes.getNum())),
                        setSuffixSpace(resultLength-batchVisConRes.getResult().length(),batchVisConRes.getResult()),
                        "success".equals(batchVisConRes.getResult()) ? 1 : 0,
                        setSuffixSpace(nameLength-batchVisConRes.getApiName().length(),batchVisConRes.getApiName())
                        );
            }

        } catch (Exception e) {
            LOGGER.error("检测文件解析异常:{}", e.getMessage());
            e.printStackTrace();
        }
    }


    @Scheduled(cron = "${execute.cron.runCommand}")
    public void executeTestTask() {
        LOGGER.info("*****开始执行prometheus生成metrics文件任务*****");
        long start = System.currentTimeMillis();
        /**
         读取yaml文件
         */
        String prefix = System.getProperty("user.dir");
        LOGGER.info("prometheus运行目录前缀:{}", prefix);
        String sep = System.getProperty("file.separator");
        /**
         * 文件路径处理：根据需求yaml文件只支持配置相对路径，统一处理，这里将配置的和xxx.yaml、/xxx.yaml转为./xxx.yaml
         */
        if (StringUtils.isBlank(command)) {
            throw new RuntimeException("");
        }
        LOGGER.info("即将执行检测模块 :{}", command);

        String directory = null;
        if (StringUtils.isNotBlank(workDir)) {
            directory = FileUtils.changePath(workDir);
            directory = prefix + sep + directory;
        }
        execute(command, directory);


        LOGGER.info("检测任务执行完成,耗时:{}ms", System.currentTimeMillis() - start);

    }


    public void execute(String command, String directory) {


        int exe;
        try {
//            LOGGER.info("===:{}",directory.endsWith(File.separator));
//            if(!directory.endsWith(File.pathSeparator)){
//                directory = directory + File.separator;
//            }


            /**
             * String[] execute = new String[]{"java", "-jar", directory+command,"--config_path="+workDir+"application.yaml"};
             *
             * 使用java -jar方式能在linux环境同样运行，但是需要注意参数需要分开
             */
            String[] execute = new String[]{"cmd.exe", "/C", command};
            LOGGER.info("执行的目录：{}，命令:{}", directory, Arrays.toString(execute));
            Process exec = null;

            try {
                Runtime rt = Runtime.getRuntime();
                if (StringUtils.isNotBlank(directory)) {
                    exec = rt.exec(execute, null, new File(directory));
                } else {
                    exec = rt.exec(execute);
                }


                new StreamThread(exec.getInputStream(), true).start();
                new StreamThread(exec.getErrorStream(), false).start();
            } catch (Exception e) {
                LOGGER.error("执行命令出现异常:{}", e.getMessage());
                e.printStackTrace();
                throw new RuntimeException("");
            }

            /**
             * 关闭子进程的输入流，不然会导致命令卡住
             */
            exec.getOutputStream().close();
            try {
                exe = exec.waitFor();
                if (exe == 0) {
                    LOGGER.info("执行成功,状态为：{}", exe);
                } else {
                    LOGGER.error("执行失败,状态：{}", exe);
                }

            } catch (Exception e) {
                LOGGER.error("获取命令执行状态失败:{}", e.getMessage());
                e.printStackTrace();
                throw new RuntimeException("");
            }
            exec.destroy();
        } catch (Exception e) {
            e.printStackTrace();
            LOGGER.error("检测失败，异常信息：{}", e.getMessage());
        }
    }

    public void getBuilder(BufferedReader bw, StringBuilder builder) {
        try {
            String input;
            while ((input = bw.readLine()) != null) {
                builder.append(input).append("\n");
            }
        } catch (Exception e) {
            LOGGER.error("命令执行异常:{}", e.getMessage());
        } finally {
            try {
                bw.close();
            } catch (Exception e) {
                LOGGER.error("关闭异常:{}", e.getMessage());
            }

        }

    }

    public String setSuffixSpace(Integer spaceSize, String param) {
        if (StringUtils.isBlank(param)) {
            param = "";
        }
        for (int i = 0; i < spaceSize; i++) {
            param += "\u0020";
        }
        return param;

    }
    public static String setPrefixSpace(Integer spaceSize,String param){
        if(StringUtils.isBlank(param)){
            param = "";
        }

        StringBuilder builder = new StringBuilder();
        for(int i=0;i<spaceSize;i++){
            builder.append("\u0020");
        }
        builder.append(param);

        return builder.toString();

    }

}
